package com.zyw.configuration;

import com.zyw.mvc.exception.handler.RestExceptionHandlerResolver;
import com.zyw.mvc.sitemesh.SiteMeshConfigurableFilter;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.freemarker.FreeMarkerProperties;
import org.springframework.boot.autoconfigure.web.ConditionalOnEnabledResourceChain;
import org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainer;
import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.web.servlet.ErrorPage;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.ui.freemarker.FreeMarkerConfigurationFactory;
import org.springframework.web.accept.ContentNegotiationManager;
import org.springframework.web.filter.CharacterEncodingFilter;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.mvc.ParameterizableViewController;
import org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver;
import org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver;
import org.springframework.web.servlet.resource.ResourceUrlEncodingFilter;
import org.springframework.web.servlet.view.freemarker.FreeMarkerConfig;
import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer;
import org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver;

import java.util.List;
import java.util.Locale;
import java.util.Properties;

/**
 * @author zhangyw
 * @date 2017/12/15 15:43
 */
public class SpringConfiguration extends WebMvcConfigurerAdapter {

    private ApplicationContext applicationContext;

    private List<HttpMessageConverter<?>> converters;

    @Autowired
    public void setApplicationContext(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }

    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        super.configureMessageConverters(converters);
        this.converters = converters;
    }

    /**
     * {@inheritDoc}
     * <p>This implementation is empty.
     */
    @Override
    public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {
        RestExceptionHandlerResolver exceptionHandlerResolver = new RestExceptionHandlerResolver();
        exceptionHandlerResolver.setContentNegotiationManager(this.applicationContext.getBean(ContentNegotiationManager.class));
        exceptionHandlerResolver.setMessageConverters(this.converters);
        exceptionHandlerResolver.setApplicationContext(this.applicationContext);
        exceptionHandlerResolver.afterPropertiesSet();
        exceptionResolvers.add(exceptionHandlerResolver);

        ResponseStatusExceptionResolver responseStatusResolver = new ResponseStatusExceptionResolver();
        responseStatusResolver.setMessageSource(this.applicationContext);
        exceptionResolvers.add(responseStatusResolver);

        exceptionResolvers.add(new DefaultHandlerExceptionResolver());
    }

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/static/libs/**")
                .addResourceLocations(
                        "classpath:/META-INF/resources/webjars/");
        registry.addResourceHandler("/static/**")
                .addResourceLocations(
                        "classpath:/static/");
    }

    /**
     * 配置http 异常code，对应的json格式代码
     *
     * @return EmbeddedServletContainerCustomizer
     */
    @Bean
    public EmbeddedServletContainerCustomizer containerCustomizer() {

        return new EmbeddedServletContainerCustomizer() {
            @Override
            public void customize(ConfigurableEmbeddedServletContainer container) {

                ErrorPage error400Page = new ErrorPage(HttpStatus.BAD_REQUEST, "/error/400");
                ErrorPage error401Page = new ErrorPage(HttpStatus.UNAUTHORIZED, "/error/401");
                ErrorPage error403Page = new ErrorPage(HttpStatus.FORBIDDEN, "/error/403");
                ErrorPage error404Page = new ErrorPage(HttpStatus.NOT_FOUND, "/error/404");
                ErrorPage error405Page = new ErrorPage(HttpStatus.METHOD_NOT_ALLOWED, "/error/405");
                ErrorPage error500Page = new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR, "/error/500");

                container.addErrorPages(error400Page, error401Page, error403Page, error404Page, error405Page, error500Page);
            }
        };
    }

    @Bean
    public FilterRegistrationBean sitemeshFilter() {
        FilterRegistrationBean filterRegistration = new FilterRegistrationBean();
        filterRegistration.setFilter(new SiteMeshConfigurableFilter());
        //  该值缺省为false,表示生命周期由SpringApplicationContext管理,设置为true则表示由ServletContainer管理
        filterRegistration.addInitParameter("targetFilterLifecycle", "true");
        filterRegistration.setEnabled(true);
        filterRegistration.addUrlPatterns("*.html");
        return filterRegistration;
    }

    @Bean
    public FilterRegistrationBean characterFilter() {
        FilterRegistrationBean filterRegistration = new FilterRegistrationBean();
        CharacterEncodingFilter encodingFilter = new CharacterEncodingFilter();
        encodingFilter.setEncoding("UTF-8");
        encodingFilter.setForceEncoding(true);
        filterRegistration.setFilter(encodingFilter);
        //  该值缺省为false,表示生命周期由SpringApplicationContext管理,设置为true则表示由ServletContainer管理
        filterRegistration.addInitParameter("targetFilterLifecycle", "true");
        filterRegistration.setEnabled(true);
        filterRegistration.addUrlPatterns("/*");
        return filterRegistration;
    }

    @Configuration
    @ConfigurationProperties(prefix = "spring.freemarker")
    public static class FreeMarkerWebConfiguration extends FreeMarkerProperties {

        private void applyProperties(FreeMarkerConfigurationFactory factory) {
            factory.setTemplateLoaderPaths(getTemplateLoaderPath());
            factory.setPreferFileSystemAccess(isPreferFileSystemAccess());
            factory.setDefaultEncoding(getCharsetName());
            Properties settings = new Properties();
            settings.putAll(getSettings());
            factory.setFreemarkerSettings(settings);
        }

        @Bean
        @ConditionalOnMissingBean(FreeMarkerConfig.class)
        public FreeMarkerConfigurer freeMarkerConfigurer() {
            FreeMarkerConfigurer configurer = new FreeMarkerConfigurer();
            applyProperties(configurer);
            return configurer;
        }

        @Bean
        public freemarker.template.Configuration freeMarkerConfiguration(
                FreeMarkerConfig configurer) {
            return configurer.getConfiguration();
        }

        /**
         * 用 freemarkerViewResolver 替换 ContentNegotiatingViewResolver
         * 规避 ContentNegotiatingViewResolver 多次查找view引起多次 freemarker查询
         *
         * @see org.springframework.web.servlet.view.ContentNegotiatingViewResolver#resolveViewName(String, Locale)
         * @see org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration.WebMvcAutoConfigurationAdapter#viewResolver(BeanFactory)
         */
        @Bean
        @ConditionalOnMissingBean(name = "viewResolver")
        @ConditionalOnProperty(name = "spring.freemarker.enabled", matchIfMissing = true)
        public FreeMarkerViewResolver freeMarkerViewResolver() {
            FreeMarkerViewResolver resolver = new FreeMarkerViewResolver();
            applyToViewResolver(resolver);
            resolver.setOrder(Ordered.HIGHEST_PRECEDENCE);
            return resolver;
        }

        @Bean
        @ConditionalOnMissingBean
        @ConditionalOnEnabledResourceChain
        public ResourceUrlEncodingFilter resourceUrlEncodingFilter() {
            return new ResourceUrlEncodingFilter();
        }

    }

//    @Bean
//    public ParameterizableViewController indexViewController(){
//        ParameterizableViewController indexView = new ParameterizableViewController();
//        indexView.setViewName("redirect:/index.html");
//        return indexView;
//    }
}
